package com.gitee.l0km.beanfilter.interceptor.thrift;

import java.lang.reflect.Method;
import java.util.Map;

import com.facebook.swift.codec.ThriftCodec;
import com.facebook.swift.codec.ThriftCodecManager;
import com.facebook.swift.service.ContextChain;
import com.facebook.swift.service.ThriftEventHandler;
import com.facebook.swift.service.ThriftMethodProcessor;
import com.facebook.swift.service.metadata.ThriftMethodMetadata;
import com.gitee.l0km.beanfilter.core.CodecScope;
import com.gitee.l0km.beanfilter.core.Filterable;
import com.gitee.l0km.beanfilter.core.ThrowingFunction;
import com.gitee.l0km.beanfilter.interceptor.FilterContainerManager;
import com.google.common.util.concurrent.ListenableFuture;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;;

/**
 * 实现在服务请求解析之前为请求方法安装字段过滤器，方法调用结束删除过滤器
 * @author guyadong
 */
public class FilterableHandler extends ThriftEventHandler {
	/** 方法全名与实际服务方法的映射 */
	private Map<String, ThriftMethodMetadata> methods;
	private ThriftCodecManager codecManager;
	private final FilterContainerManager containerManager = FilterContainerManager.getInstance();
	private final ThrowingFunction<Class<?>,Filterable,RuntimeException> TO_FILTERABLE = clazz->{
		try {
			checkState(null != codecManager,"codecManager is null");
			ThriftCodec<?> codec = codecManager.getCodec(clazz);
			return (codec instanceof Filterable)?(Filterable) codec : null;
		} catch (RuntimeException e) {
			/** 
			 * 如果注解定义的beanClass类型是抽象类或接口类，
			 * associateClasses()返回的关联类型有可能不是RPC接口涉及的类型或不支持的类型，
			 * 造成 getCodec抛出异常，所以这里忽略异常，继续循环
			 */
			return null;
		}
	};
	/**
	 * 将hander 绑定到指定的{@link ThriftCodecManager},该方法不允许重复调用<br>
	 * 该方法会以反射方式被调用，参见 {@code com.facebook.swift.service.XthriftServiceProcessor.invokeBind}方法
	 * @param codecManager
	 * @param methods
	 */
	public void bind(ThriftCodecManager codecManager, Map<String, ThriftMethodMetadata> methods) {
		checkState(null == this.methods,"methods is initialized,Reject initizlied repeatly");
		this.codecManager = checkNotNull(codecManager,"codecManager is null");
		this.methods = checkNotNull(methods,"methods is null");
	}
	private Method methodOf(String methodName) {
		return checkNotNull(methods,"methods is uninitialized").get(methodName).getMethod();
	}
	/**
	 * 根据服务方法返回类型判断方法是否会异步调用
	 * @param methodName
	 */
	private boolean isAsync(String methodName) {
		return ListenableFuture.class.isAssignableFrom(methodOf(methodName).getReturnType());
	}
	/**
	 * 获取方法的过滤器容器对象，注入到编解码器中
	 */
	@Override
	public void preRead(Object context, String methodName) {
		containerManager.installIfAbsent(methodOf(methodName), TO_FILTERABLE, TO_FILTERABLE, CodecScope.THRIFT);
	}
	/***
	 * 当前线程中{@link ThriftEventHandler}方法的调用顺序为:  {@code getContext,preRead,postRead},
	 * 后续调用可能是异步的，
	 * 如果服务方法为异步，返回类型为{@link com.google.common.util.concurrent.ListenableFuture},则后续接口方法 
	 * {@code preWrite,preWriteException,postWrite,postWriteException,done}在异步线程中调用。
	 * 所以在这里判断服务方法返回类型，如果是{@link com.google.common.util.concurrent.ListenableFuture}则清理线程局部变量
	 * @see ThriftMethodProcessor#process(org.apache.thrift.protocol.TProtocol, org.apache.thrift.protocol.TProtocol, int, ContextChain)
	 * @see ThriftMethodProcessor#invokeMethod(Object[])
	 */
	@Override
	public void postRead(Object context, String methodName, Object[] args) {
		if(isAsync(methodName)) {
			containerManager.clearLocal();
		}
	}
	/***
	 * 如果是服务方法是异步调用，则重新安装过滤到线程局部变量
	 * @see #postRead(Object, String, Object[])
	 */
	@Override
	public void preWrite(Object context, String methodName, Object result) {
		if(isAsync(methodName)) {
			containerManager.installIfAbsent(methodOf(methodName), TO_FILTERABLE, TO_FILTERABLE, CodecScope.THRIFT);
		}
	}

	/***
	 * 调用结束清除线程局部变量
	 */
	@Override
	public void done(Object context, String methodName) {
		containerManager.clearLocal();
	}
}
